package com.vhly.ddxdecompiler.dexreader.file.mixeditem;

import com.vhly.classfile.*;
import com.vhly.classfile.util.CPTool;
import com.vhly.ddxdecompiler.dexreader.file.DexFile;
import com.vhly.ddxdecompiler.dexreader.file.constant.CstLiteralBits;
import com.vhly.ddxdecompiler.dexreader.file.constant.CstString;
import com.vhly.ddxdecompiler.dexreader.file.constant.CstType;
import com.vhly.ddxdecompiler.dexreader.file.mixeditem.annotation.FieldAnnotationStruct;
import com.vhly.ddxdecompiler.dexreader.file.mixeditem.annotation.MethodAnnotationStruct;
import com.vhly.ddxdecompiler.dexreader.file.mixeditem.annotation.ParameterAnnotationStruct;
import com.vhly.ddxdecompiler.dexreader.io.DexInputStream;
import com.vhly.ddxdecompiler.dexreader.util.StringUtil;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Vector;

/**
 * Created by IntelliJ IDEA.
 * User: vhly
 * Date: 2009-10-15
 * Time: 12:38:22
 *
 * @author vhly[FR]
 */
public final class AnnotationsDirectoryItem extends OffsettedItem {

    private DexFile dex;

    private int classOff;

    private AnnotationSetItem classAnnotations;
    private ArrayList<FieldAnnotationStruct> fieldAnnotations;
    private ArrayList<MethodAnnotationStruct> methodAnnotations;
    private ArrayList<ParameterAnnotationStruct> parameterAnnotations;

    public AnnotationsDirectoryItem(DexFile dexFile) {
        super(4, 0);
        dex = dexFile;
    }

    /**
     * 读取数据
     *
     * @param din 输入流
     * @throws java.io.IOException 异常
     */
    public void read(DexInputStream din) throws IOException {
        offset = din.getOffset();
        dex.offsetCache.put(Integer.toString(offset), this);

        classOff = din.readInt();
        int fieldsSize = din.readInt();
        int methodsSize = din.readInt();
        int parametersSize = din.readInt();

        if (fieldsSize > 0) {
            fieldAnnotations = new ArrayList<FieldAnnotationStruct>();
            FieldAnnotationStruct item;
            for (int i = 0; i < fieldsSize; i++) {
                item = new FieldAnnotationStruct(dex);
                item.read(din);
                fieldAnnotations.add(item);
            }
        }
        if (methodsSize > 0) {
            methodAnnotations = new ArrayList<MethodAnnotationStruct>();
            MethodAnnotationStruct item;
            for (int i = 0; i < fieldsSize; i++) {
                item = new MethodAnnotationStruct(dex);
                item.read(din);
                methodAnnotations.add(item);
            }
        }
        if (parametersSize > 0) {
            parameterAnnotations = new ArrayList<ParameterAnnotationStruct>();
            ParameterAnnotationStruct item;
            for (int i = 0; i < fieldsSize; i++) {
                item = new ParameterAnnotationStruct(dex);
                item.read(din);
                parameterAnnotations.add(item);
            }
        }

    }

    /**
     * 输出数据
     *
     * @param dout 输出流
     * @throws java.io.IOException 异常
     */
    public void write(DataOutputStream dout) throws IOException {
    }

    public void reloc(ByteArrayInputStream bin, int offset) throws IOException {

        bin.reset();

        if (classOff != 0) {
            int off = classOff;
            classAnnotations = new AnnotationSetItem(dex);
            bin.reset();
            bin.skip(off - offset);
            DataInputStream din = new DataInputStream(bin);
            DexInputStream dxin = new DexInputStream(din, 1);
            dxin.setOffset(off);
            classAnnotations.read(dxin);
            classAnnotations.reloc(bin, offset);

            dxin.close();
            din.close();
        }

        if (fieldAnnotations != null) {
            FieldAnnotationStruct item;
            int fieldsSize = fieldAnnotations.size();
            for (int i = 0; i < fieldsSize; i++) {
                item = fieldAnnotations.get(i);
                item.reloc(bin, offset);
            }
        }

        if (methodAnnotations != null) {
            MethodAnnotationStruct item;
            int fieldsSize = methodAnnotations.size();
            for (int i = 0; i < fieldsSize; i++) {
                item = methodAnnotations.get(i);
                item.reloc(bin, offset);
            }
        }

        if (parameterAnnotations != null) {
            ParameterAnnotationStruct item;
            int fieldsSize = parameterAnnotations.size();
            for (int i = 0; i < fieldsSize; i++) {
                item = parameterAnnotations.get(i);
                item.reloc(bin, offset);
            }
        }
    }

    public void reverseClass(JavaClass clazz) {
        if(classAnnotations != null){
            Annotation enclosingClass = classAnnotations.getItemByType("Ldalvik/annotation/EnclosingClass;");
            Annotation innerClass = classAnnotations.getItemByType("Ldalvik/annotation/InnerClass;");
            if(enclosingClass != null && innerClass != null){
                ConstantPool pool = clazz.getCP();
                int ni = CPTool.addUTF8(pool, "InnerClasses");
                InnerClasses inners = new InnerClasses();
                inners.setNameIndex(ni);
                NameValuePair valueType = enclosingClass.findByName("value");
                CstType type = (CstType) valueType.value;
                String outerClassName = (String) type.getJavaData();
                outerClassName = StringUtil.removeClassModify(outerClassName);
                int ci = CPTool.addClass(pool, outerClassName);
                InnerClass ic = new InnerClass();
                ic.setOC(ci);
                Vector<NameValuePair> pairs = innerClass.pairs;
                for(NameValuePair pair : pairs){
                    String pn = pair.getName();
                    System.out.println("pn = " + pn);
                    if(pn.equals("accessFlags")){
                        CstLiteralBits lib =(CstLiteralBits) pair.value;
                        ic.setIAccess(lib.getIntBits());
                    }else if(pn.equals("name")){
                        CstString cs = (CstString) pair.value;
                        String data = (String) cs.getJavaData();
                        ni = CPTool.addUTF8(pool,data);
                        ic.setIName(ni);
                        data = outerClassName + "$" + data;
                        ci = CPTool.addClass(pool,data);
                        ic.setIC(ci);
                    }
                }
                inners.addInnerClass(ic);
                clazz.setInnerClasses(inners);
            }
        }
    }
}
